﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Sgr.BillNumbering.Domain;
using Sgr.Exceptions;

namespace Sgr.BillNumbering.Services.Impl
{
    /// <summary>
    /// 不带缓存的序列生成器实现，兼容主流数据库，支持并发与自动插入
    /// </summary>
    public class DbDirectCodeSequence<TDbContext> : ICodeSequence where TDbContext : DbContext
    {
        private readonly IServiceScopeFactory _scopeFactory;
        private readonly ILogger<DbDirectCodeSequence<TDbContext>> _logger;

        //private readonly CodeOptions _options;

        public DbDirectCodeSequence(
            IServiceScopeFactory scopeFactory,
            //IOptions<CodeOptions> options,
            ILogger<DbDirectCodeSequence<TDbContext>> logger)
        {
            //_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        public async Task<long> GetNextValueAsync(
            string category,
            string customPrefix = "",
            SequenceResetStrategy resetStrategy = SequenceResetStrategy.Never,
            int? segmentSize = null,
            CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(category))
                throw new ArgumentException("Category is required.", nameof(category));

            segmentSize = 1;

            var now = DateTime.UtcNow;
            var timeKey = TimeKeyGenerator.Generate(resetStrategy, now);

            for (int attempt = 0; attempt < 5; attempt++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                try
                {
                    using var scope = _scopeFactory.CreateScope();
                    await using var dbContext = scope.ServiceProvider.GetRequiredService<TDbContext>();

                    // 1. 查询是否已存在
                    var counter = await dbContext.Set<SequenceCounter>()
                        .Where(c => c.Category == category
                                 && c.TimeKey == timeKey
                                 && c.CustomPrefix == customPrefix)
                        .FirstOrDefaultAsync(cancellationToken);

                    if (counter != null)
                    {
                        // 2. 已存在则乐观并发递增
                        var newValue = counter.AllocatedMaxValue + segmentSize.Value;
                        if (newValue > counter.MaxValue)
                            throw new BusinessException("Sequence counter has reached its maximum value.");

                        counter.AllocatedMaxValue = newValue;
                        counter.UpdatedAt = now;
                        counter.RowVersion++;
                    }
                    else
                    {
                        var expiry = TimeKeyGenerator.CalculateExpiry(resetStrategy, now);
                        // 3. 不存在则插入新行
                        counter = new SequenceCounter
                        {
                            Category = category,
                            TimeKey = timeKey,
                            CustomPrefix = customPrefix,
                            AllocatedMaxValue = 1,
                            MaxValue = int.MaxValue,
                            UpdatedAt = now,
                            ExpiryTime = expiry,
                            RowVersion = 1
                        };
                        await dbContext.Set<SequenceCounter>().AddAsync(counter, cancellationToken);
                    }

                    var affected = await dbContext.SaveChangesAsync(cancellationToken);
                    if (affected == 0)
                        continue;

                    return counter.AllocatedMaxValue;
                }
                catch (DbUpdateException ex)
                {
                    // 兼容并发插入时主键冲突（如MySQL、PG、SQLServer）
                    _logger.LogWarning(ex, "PK conflict when inserting sequence for {Category}-{TimeKey}-{CustomPrefix}, attempt {Attempt}",
                        category, timeKey, customPrefix, attempt + 1);
                    await Task.Delay(20 * (attempt + 1), cancellationToken);
                }
            }

            throw new BusinessException("Failed to allocate sequence value after concurrency retries.");
        }

        public int CleanupExpiredSegments()
        {
            // 无缓存，无需清理
            return 0;
        }

        ///// <summary>
        ///// 判断是否为唯一约束冲突（兼容主流数据库）
        ///// </summary>
        //private static bool IsUniqueConstraintViolation(DbUpdateException ex)
        //{
        //    var msg = ex.InnerException?.Message ?? ex.Message;
        //    return msg.Contains("duplicate", StringComparison.OrdinalIgnoreCase)
        //        || msg.Contains("unique", StringComparison.OrdinalIgnoreCase)
        //        || msg.Contains("PRIMARY", StringComparison.OrdinalIgnoreCase)
        //        || msg.Contains("UNIQUE", StringComparison.OrdinalIgnoreCase);
        //}
    }
}